# -*- coding: utf-8 -*-
#
# Copyright (c) 2011,2012 RMIT e-Research Office
#   (RMIT University, Australia)
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#    *  Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#    *  Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#    *  Neither the name of RMIT University nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
"""
.. module:: models
   :synopsis: Models for the SMRA system

.. moduleauthor:: Ian Thomas <Ian.Edward.Thomas@rmit.edu.au>


"""

from django.db import models
from django.contrib.auth.models import User
from django.utils.text import truncate_words
from django import forms
from django.utils.safestring import mark_safe
from django.db.models.loading import get_model


class UserProfile(models.Model):
    """ Extends Django user model to add extra user information
    
        :attribute results_per_page: number of items in standard view pages
    """
    user = models.ForeignKey(User, unique=True)
    results_per_page = models.IntegerField(default=25)
    def __unicode__(self):
        return self.user.username

    
class System(models.Model):
    """ A specific database deployment (e.g., SMRA)
    
        :attribute name: name of the deployment
    """
    name = models.CharField(max_length=80)
    def __unicode__(self):
        return self.name

    
class Repository(models.Model):
    """ A specific repository  (e.g., BONZA, AFI)
        
        :attribute name: name of the repository
        :attribute system: a foreign key to the class :class:`smra.smra_portal.models.System`
        
    """
    name = models.CharField(max_length=80)
    system = models.ForeignKey(System)
    def __unicode__(self):
        return self.name

     
# declaration of a set of parameter (e.g., XML schema)
class Schema(models.Model):
    """ Representation of a set of parameters (equiv to XML Schema) 
    
        :attribute namespace: namespace for this schema
        :attribute name: unique name
        :attribute description: displayable text describing the schema
        
    """
    namespace = models.URLField(verify_exists = False, max_length=400)
    description = models.CharField(max_length=80,default="")
        
    name = models.SlugField(default="",help_text="A unique identifier for the schema")
 
    class Meta:
        unique_together = (('namespace', 'name'),)
    
    def __unicode__(self):
        return "%s (%s)" % (self.name,self.namespace)


class MediaObject(models.Model):
    """ Representation of a single media object such as a film
        
        :attribute description: the key name or description of the object
        :attribute repos: a foreign key to the class :class:`smra.smra_portal.models.Repository`
        :attribute schemas: a many-to-many key to the class :class:`smra.smra_portal.models.Schema` via class :class:`smra.smra_portal.models.MediaObjectParameterSet`
        
        
    """
    description = models.CharField(max_length=400)
    repos = models.ForeignKey(Repository,verbose_name="Repository")
    schemas = models.ManyToManyField(Schema,through='MediaObjectParameterSet')
    #TODO: add ability to restrict editing to the owner
    #owned_by = models.ForeignKey(UserProfile)
  
    def __unicode__(self):
        return self.description
    
    def get_absolute_url(self):
        return "/smra/mediaobjects/%i/" % self.id
    #class Meta:
    #    ordering = ['description']


class Key(models.Model):
    
    """ A Repository-specific unique identifier for a media object. Required
    so that re-ingestion updates same media object.
    
        :attribute ident: identifier for associated media object
        :attribute repos: the :class:`smra.smra_portal.models.Repository` that the media object is associated with.
        :attribute media_object: the :class:`smra.smra_portal.models.MediaObject` that this key is associated with.
    """
    ident = models.CharField(default="",max_length=400)
    repos = models.ForeignKey(Repository)
    media_object = models.OneToOneField(MediaObject)
  
    def __unicode__(self):
        return "%s" % self.ident
  
    class Meta:
        ordering= ["ident"]
    

class MediaObjectParameterSet(models.Model):
    """ Association of a media object with a specific schema.
        
        :attribute media_object: the :class:`smra.smra_portal.models.MediaObject` 
        :attribute schema:  the :class:`smra.smra_portal.models.Schema`
        :attribute ranking: int which indicates relative ranking of parameter set in listings
     """
    media_object = models.ForeignKey(MediaObject, verbose_name="Media Object")
    schema = models.ForeignKey(Schema, verbose_name="Schema")
    #info = models.CharField(max_length=400, null=True)
    ranking = models.IntegerField(default=0)
      
    def __unicode__(self):
        return u'%s (%s)' % (self.media_object.description, self.schema.name)
    
    class Meta:
        ordering = ["-ranking"]
    

class ParameterName(models.Model):
    """ A parameter associated with a schema
    
        :attribute schema: the  :class:`smra.smra_portal.models.Schema` which this parameter belongs to
        :attribute name: the name of the parameter
        :attribute type: the type of the parameter from TYPES
        :attribute ranking: int which indicates relative ranking in listings
        :attribute initial: any initial value for this parameter
        :attribute choices: a serialised python list of string choices for the STRLIST type
        :attribute help_text: text that appears in admin tool
        :attribute max_length: maximum length for STRING types
    """
    schema = models.ForeignKey(Schema)
    name = models.CharField(max_length=50)
    # TODO: need to do this so that each paramter can appear only once
    # in each schema
    class Meta:
        unique_together = (('schema', 'name'),)
        ordering=["-ranking"]
    UNKNOWN = 0  
    STRING = 1
    NUMERIC = 2 # only integers    
    LINK = 3
    STRLIST = 4
    DATE = 5
    YEAR = 6
    TYPES = (
             (UNKNOWN, 'UNKNOWN'),
             (STRING, 'STRING'),
             (NUMERIC,'NUMERIC'),
             (LINK,'LINK'),
             (STRLIST,'STRLIST'),
             (DATE,'DATE'),
             (YEAR,'YEAR')
             
            ) 
    # The form used to store dates in the DATE type field
    DATE_FORMAT = "%b %d, %Y"
    type = models.IntegerField(choices=TYPES, default=STRING)
    ranking = models.IntegerField(default=0,
                                  help_text="Describes the relative ordering "
                                  "of parameters when displaying: the larger "
                                  "the number, the more prominent the results")
    initial = models.TextField(default="",blank=True,
                               verbose_name="Initial Value", 
                             help_text= "The initial value for this parameter")
    choices = models.TextField(default="",blank=True,
                               verbose_name="Choices for the field")
    help_text = models.TextField(default="",blank=True,
                                 verbose_name="Text to help user fill out "
                                              "the field")
    max_length = models.IntegerField(default=255,
                                     verbose_name="Maximum number of "
                                     "characters in a parameter")
    
    def __unicode__(self):
        return u'%s (%s)' %  (truncate_words(self.name,6),self.schema.name) 
                              
    #TODO: make a method to display schema and parameters as XML schema definition       
    def get_type_string(self,val):
        for (t,str) in self.TYPES:
            if t == val:
                return str
        return "UNKNOWN"
    
    
class MediaObjectParameter(models.Model):
    """ The value for some metadata for a media object
    
        :parameter name: the associated  :class:`smra.smra_portal.models.ParameterName` that the value matches to
        :parameter paramset: associated  :class:`smra.smra_portal.models.MediaObject` and class:`smra.smra_portal.models.Schema` for this value
        :parameter value: the actual value
    """
    name = models.ForeignKey(ParameterName, verbose_name="Parameter Name")
    paramset = models.ForeignKey(MediaObjectParameterSet, verbose_name="Parameter Set")
    value = models.TextField(verbose_name="Parameter Value", help_text= "The Value of this parameter")
    #ranking = models.IntegerField(default=0,help_text="Describes the relative ordering of parameters when displaying: the larger the number, the more prominent the results")

    def __unicode__(self): 
        return u'%s %s %s' % (self.name, self.paramset, truncate_words(self.value,6))

    class Meta:
        ordering = ("name",)
    
    
class DeadLink(models.Model):
    """ A record that describes a broken LINK in a media object metadata
    
        :parameter parameter: the :class:`smra.smra_portal.models.MediaObjectParameter` with the problem
        :parameter date_created: when problem was found
        :parameter date_accessed: when last checked
        :parameter reason: the reported problem
    """
    
    parameter = models.OneToOneField(MediaObjectParameter)
    date_created = models.DateTimeField(auto_now_add=True)
    date_accessed = models.DateTimeField(auto_now=True)
    reason = models.CharField(max_length=400)

    def __unicode__(self):
        return "%s %s" % (self.parameter, self.reason)

    
class VirtualCollection(models.Model):
    """ A virtual collection of media objects and associated metadata
    
        :parameter owned_by: which user owns the collection
        :parameter name: name of the collection
        :parameter key: unique key used by RIFCS generation
        :parameter description: plain text summary of the collection
        :parameter definition: the Disjunction definition of the media object set for this collection
        :parameter public: is this collection viewable to everone, including feeds
        :parameter created_time: first definition
        :parameter update_time: last time changed
        :parameter ingested_time: last time this public collecton was taken from feed
    """
    owned_by = models.ForeignKey(UserProfile)
    name = models.CharField(max_length=400,default="")
    key = models.CharField(max_length=400,default="",blank=True,help_text="This is a unique identifier (used for rifcs generation)")
    description = models.TextField(blank=True,default="",help_text="The plain text description of the virtual collection")
    definition = models.TextField(default="",blank=True,help_text="The equation that describes the set of media objects in this creation")   
    #definition = models.CharField(max_length=400,default="")   
    public = models.BooleanField(default=False, help_text="Indicates that this virtual collection can be viewed by public and ingested by ANDS.  Use with caution") 
    created_time = models.DateTimeField(auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True,blank=True)
    ingested_time = models.DateTimeField(null=True,blank=True,help_text="Indicates the last time this record was taken by ANDS")
    
    def __unicode__(self):
        return "%s:%s" % (self.pk,self.name)
    
    def profile(self):
        
        profile_template_location = "rif_cs_profile/profiles/"
        
        try:
            from smra.smra_portal.publish.rif_cs_profile.rif_cs_PublishProvider import rif_cs_PublishProvider
            
            rif_cs_pp = rif_cs_PublishProvider(self.pk)
            
            profile = rif_cs_pp.get_profile()
            if not profile:            
                return profile_template_location + "default.xml"
            return profile_template_location + profile
        except:
            return profile_template_location + "default.xml"


class VirtualCollectionHarvest(models.Model):
    """ A record of when the associated virtual collection was last harvested
    
        :parameter virt: the collection
        :parameter ingested_time: when associated rifcs was last taken
    """
    
    virt = models.OneToOneField(VirtualCollection)
    ingested_time = models.DateTimeField(null=True)
    
    def __unicode__(self):
        return "%s:%s" % (self.virt.pk,self.ingested_time)
    
         
class VirtualCollectionParameterSet(models.Model):  
    """ Association of a virtual collection with a specific schema.
        
        :attribute virt: the :class:`smra.smra_portal.models.VirtualCollection` 
        :attribute schema:  the :class:`smra.smra_portal.models.Schema`
        :attribute ranking: int which indicates relative ranking of parameter set in listings
    """
    virt = models.ForeignKey(VirtualCollection, 
                             verbose_name="Virtual Collection",help_text="The virtual colleciton that this parameter set belongs to")
    schema = models.ForeignKey(Schema, verbose_name="Schema")
    ranking = models.IntegerField(default=0)
      
    def __unicode__(self):
        return u'#%s %s (%s/%s)' % (self.virt.pk, self.virt.name, 
                                    self.schema.namespace, self.schema.name)
    
    class Meta:
        ordering = ["-ranking"]
    
class VirtualCollectionParameter(models.Model):
    """ The value for some metadata for a collection
    
        :parameter name: the associated  :class:`smra.smra_portal.models.ParameterName` that the value matches to
        :parameter paramset: associated  :class:`smra.smra_portal.models.VirtualCollection` and class:`smra.smra_portal.models.Schema` for this value
        :parameter value: the actual value
    """
    
    name = models.ForeignKey(ParameterName, 
                             verbose_name="Parameter Name")
    paramset = models.ForeignKey(VirtualCollectionParameterSet,
                                  verbose_name="Parameter Set")
    value = models.TextField(verbose_name="Parameter Value", 
                             help_text= "The Value of this parameter")
    #ranking = models.IntegerField(default=0,help_text="Describes the relative ordering of parameters when displaying: the larger the number, the more prominent the results")

    def __unicode__(self): 
        return u'%s %s %s' % (self.name, self.paramset, 
                              truncate_words(self.value,6))

    class Meta:
        ordering = ("name",)
        

class HiddenInputWithText(forms.TextInput):
    input_type = 'hidden'

    def __init__(self, *args, **kwargs):
        super(HiddenInputWithText, self).__init__(*args, **kwargs)
        self.attrs = kwargs.get('attrs', {})
        self.key = self.attrs.get('key', False)
        
    def render(self, name, value, *args, **kwargs):
        model = get_model('smra_portal', name)
        if self.key:
            text = unicode(model.objects.get(pk=value)) if value is not None and value != '' else '-----'
        else:
            text = value
        html = '%s %s' % (super(HiddenInputWithText, 
                                self).render(name, value, self.attrs), text)
        return mark_safe(html)
        

 
class Subquery(models.Model):
    """ A subcollection which can be combined through union into larger collections
        
        :parameter owned_by: the user who owns the subquery
        :parameter part_of: which collection contains this query
        :parameter name: name for the query
        :parameter query: definition of the query
        
    """
    owned_by = models.ForeignKey(UserProfile)
    part_of = models.ForeignKey(VirtualCollection)
    name = models.CharField(max_length=80)
    query = models.TextField()
    
    def __unicode__(self):
        return self.name
    
    
class PartyRecord(models.Model):
    """ The required information for a RIFCS party record
    
        :attribute key:
        :attribute type:
        :attribute name:
        :attribute birthdate:
        :attribute deathdate:
        :attribute source:
        :attribute subject:
        :attribute electronic_location:
        :attribute physical_location:
        :attribute brief_description: The party information rendered as RIFC.  See smra.srma_portal.publish.rif_cs_profile.rif-cs.xml
    
    """
    
    key = models.CharField(max_length=200,default="",blank=True)
    type = models.CharField(max_length=80,default="")
    name = models.CharField(max_length=200,default="")
    birthdate = models.DateField(null=True, blank=True)
    deathdate = models.DateField(null=True, blank=True)
    source = models.CharField(max_length=200,default="",blank=True)
    subject = models.TextField(default="",blank=True)
    electronic_location = models.TextField(default="",blank=True)
    physical_location = models.TextField(default="",blank=True)
    brief_description = models.TextField(default="",blank=True)
    
    def __unicode__(self):
        return self.name
    
    
class ActivityRecord(models.Model):
    # not used
    type = models.CharField(max_length=80)
    name = models.CharField(max_length=200)
                 
